{
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.444644Z",
          "start_time": "2025-01-17T03:09:34.440225Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "skbGHtP5c0z5",
        "outputId": "ae38762a-1ae4-49c2-91ea-54fbb180c78b"
      },
      "source": [
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "%matplotlib inline\n",
        "import numpy as np\n",
        "import sklearn\n",
        "import pandas as pd\n",
        "import os\n",
        "import sys\n",
        "import time\n",
        "from tqdm.auto import tqdm\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "\n",
        "print(sys.version_info)\n",
        "for module in mpl, np, pd, sklearn, torch:\n",
        "    print(module.__name__, module.__version__)\n",
        "\n",
        "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
        "print(device)\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "sys.version_info(major=3, minor=10, micro=12, releaselevel='final', serial=0)\n",
            "matplotlib 3.8.0\n",
            "numpy 1.26.4\n",
            "pandas 2.2.2\n",
            "sklearn 1.6.0\n",
            "torch 2.5.1+cu121\n",
            "cuda:0\n"
          ]
        }
      ],
      "execution_count": 2
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tO291eyfc0z9"
      },
      "source": [
        "## 准备数据"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.488978Z",
          "start_time": "2025-01-17T03:09:34.481479Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "iBIPZgLTc0z_",
        "outputId": "10e77eac-530d-42ed-8e76-ed630c223add"
      },
      "source": [
        "from sklearn.datasets import fetch_california_housing\n",
        "\n",
        "housing = fetch_california_housing(data_home='data')\n",
        "print(housing.DESCR)\n",
        "print(housing.data.shape)\n",
        "print(housing.target.shape)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            ".. _california_housing_dataset:\n",
            "\n",
            "California Housing dataset\n",
            "--------------------------\n",
            "\n",
            "**Data Set Characteristics:**\n",
            "\n",
            ":Number of Instances: 20640\n",
            "\n",
            ":Number of Attributes: 8 numeric, predictive attributes and the target\n",
            "\n",
            ":Attribute Information:\n",
            "    - MedInc        median income in block group\n",
            "    - HouseAge      median house age in block group\n",
            "    - AveRooms      average number of rooms per household\n",
            "    - AveBedrms     average number of bedrooms per household\n",
            "    - Population    block group population\n",
            "    - AveOccup      average number of household members\n",
            "    - Latitude      block group latitude\n",
            "    - Longitude     block group longitude\n",
            "\n",
            ":Missing Attribute Values: None\n",
            "\n",
            "This dataset was obtained from the StatLib repository.\n",
            "https://www.dcc.fc.up.pt/~ltorgo/Regression/cal_housing.html\n",
            "\n",
            "The target variable is the median house value for California districts,\n",
            "expressed in hundreds of thousands of dollars ($100,000).\n",
            "\n",
            "This dataset was derived from the 1990 U.S. census, using one row per census\n",
            "block group. A block group is the smallest geographical unit for which the U.S.\n",
            "Census Bureau publishes sample data (a block group typically has a population\n",
            "of 600 to 3,000 people).\n",
            "\n",
            "A household is a group of people residing within a home. Since the average\n",
            "number of rooms and bedrooms in this dataset are provided per household, these\n",
            "columns may take surprisingly large values for block groups with few households\n",
            "and many empty houses, such as vacation resorts.\n",
            "\n",
            "It can be downloaded/loaded using the\n",
            ":func:`sklearn.datasets.fetch_california_housing` function.\n",
            "\n",
            ".. rubric:: References\n",
            "\n",
            "- Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions,\n",
            "  Statistics and Probability Letters, 33 (1997) 291-297\n",
            "\n",
            "(20640, 8)\n",
            "(20640,)\n"
          ]
        }
      ],
      "execution_count": 3
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.492238Z",
          "start_time": "2025-01-17T03:09:34.488978Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ZvhiLMFTc00A",
        "outputId": "6479dacb-349e-4393-9b87-278ff63d6224"
      },
      "source": [
        "# print(housing.data[0:5])\n",
        "import pprint  #打印的格式比较 好看\n",
        "\n",
        "pprint.pprint(housing.data[0:5])\n",
        "print('-'*50)\n",
        "pprint.pprint(housing.target[0:5])"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "array([[ 8.32520000e+00,  4.10000000e+01,  6.98412698e+00,\n",
            "         1.02380952e+00,  3.22000000e+02,  2.55555556e+00,\n",
            "         3.78800000e+01, -1.22230000e+02],\n",
            "       [ 8.30140000e+00,  2.10000000e+01,  6.23813708e+00,\n",
            "         9.71880492e-01,  2.40100000e+03,  2.10984183e+00,\n",
            "         3.78600000e+01, -1.22220000e+02],\n",
            "       [ 7.25740000e+00,  5.20000000e+01,  8.28813559e+00,\n",
            "         1.07344633e+00,  4.96000000e+02,  2.80225989e+00,\n",
            "         3.78500000e+01, -1.22240000e+02],\n",
            "       [ 5.64310000e+00,  5.20000000e+01,  5.81735160e+00,\n",
            "         1.07305936e+00,  5.58000000e+02,  2.54794521e+00,\n",
            "         3.78500000e+01, -1.22250000e+02],\n",
            "       [ 3.84620000e+00,  5.20000000e+01,  6.28185328e+00,\n",
            "         1.08108108e+00,  5.65000000e+02,  2.18146718e+00,\n",
            "         3.78500000e+01, -1.22250000e+02]])\n",
            "--------------------------------------------------\n",
            "array([4.526, 3.585, 3.521, 3.413, 3.422])\n"
          ]
        }
      ],
      "execution_count": 4
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.501111Z",
          "start_time": "2025-01-17T03:09:34.495746Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "hxZGDAutc00B",
        "outputId": "3848718c-62de-4b15-db69-884a155b9f52"
      },
      "source": [
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "#拆分训练集和测试集，random_state是随机种子,同样的随机数种子，是为了得到同样的随机值\n",
        "x_train_all, x_test, y_train_all, y_test = train_test_split(\n",
        "    housing.data, housing.target, random_state = 7)\n",
        "x_train, x_valid, y_train, y_valid = train_test_split(\n",
        "    x_train_all, y_train_all, random_state = 11)\n",
        "# 训练集\n",
        "print(x_train.shape, y_train.shape)\n",
        "# 验证集\n",
        "print(x_valid.shape, y_valid.shape)\n",
        "# 测试集\n",
        "print(x_test.shape, y_test.shape)\n",
        "\n",
        "dataset_maps = {\n",
        "    \"train\": [x_train, y_train],\n",
        "    \"valid\": [x_valid, y_valid],\n",
        "    \"test\": [x_test, y_test],\n",
        "}\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(11610, 8) (11610,)\n",
            "(3870, 8) (3870,)\n",
            "(5160, 8) (5160,)\n"
          ]
        }
      ],
      "execution_count": 5
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.506115Z",
          "start_time": "2025-01-17T03:09:34.501111Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 79
        },
        "id": "fQ69syJLc00C",
        "outputId": "3092c317-9bcd-4227-ee27-83bf6c3f57d4"
      },
      "source": [
        "from sklearn.preprocessing import StandardScaler\n",
        "from torch.utils.data import DataLoader\n",
        "\n",
        "\n",
        "scaler = StandardScaler()\n",
        "scaler.fit(x_train)"
      ],
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StandardScaler()"
            ],
            "text/html": [
              "<style>#sk-container-id-1 {\n",
              "  /* Definition of color scheme common for light and dark mode */\n",
              "  --sklearn-color-text: #000;\n",
              "  --sklearn-color-text-muted: #666;\n",
              "  --sklearn-color-line: gray;\n",
              "  /* Definition of color scheme for unfitted estimators */\n",
              "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
              "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
              "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
              "  --sklearn-color-unfitted-level-3: chocolate;\n",
              "  /* Definition of color scheme for fitted estimators */\n",
              "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
              "  --sklearn-color-fitted-level-1: #d4ebff;\n",
              "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
              "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
              "\n",
              "  /* Specific color for light theme */\n",
              "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
              "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
              "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
              "  --sklearn-color-icon: #696969;\n",
              "\n",
              "  @media (prefers-color-scheme: dark) {\n",
              "    /* Redefinition of color scheme for dark theme */\n",
              "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
              "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
              "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
              "    --sklearn-color-icon: #878787;\n",
              "  }\n",
              "}\n",
              "\n",
              "#sk-container-id-1 {\n",
              "  color: var(--sklearn-color-text);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 pre {\n",
              "  padding: 0;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 input.sk-hidden--visually {\n",
              "  border: 0;\n",
              "  clip: rect(1px 1px 1px 1px);\n",
              "  clip: rect(1px, 1px, 1px, 1px);\n",
              "  height: 1px;\n",
              "  margin: -1px;\n",
              "  overflow: hidden;\n",
              "  padding: 0;\n",
              "  position: absolute;\n",
              "  width: 1px;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-dashed-wrapped {\n",
              "  border: 1px dashed var(--sklearn-color-line);\n",
              "  margin: 0 0.4em 0.5em 0.4em;\n",
              "  box-sizing: border-box;\n",
              "  padding-bottom: 0.4em;\n",
              "  background-color: var(--sklearn-color-background);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-container {\n",
              "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
              "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
              "     so we also need the `!important` here to be able to override the\n",
              "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
              "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
              "  display: inline-block !important;\n",
              "  position: relative;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-text-repr-fallback {\n",
              "  display: none;\n",
              "}\n",
              "\n",
              "div.sk-parallel-item,\n",
              "div.sk-serial,\n",
              "div.sk-item {\n",
              "  /* draw centered vertical line to link estimators */\n",
              "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
              "  background-size: 2px 100%;\n",
              "  background-repeat: no-repeat;\n",
              "  background-position: center center;\n",
              "}\n",
              "\n",
              "/* Parallel-specific style estimator block */\n",
              "\n",
              "#sk-container-id-1 div.sk-parallel-item::after {\n",
              "  content: \"\";\n",
              "  width: 100%;\n",
              "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
              "  flex-grow: 1;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-parallel {\n",
              "  display: flex;\n",
              "  align-items: stretch;\n",
              "  justify-content: center;\n",
              "  background-color: var(--sklearn-color-background);\n",
              "  position: relative;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-parallel-item {\n",
              "  display: flex;\n",
              "  flex-direction: column;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
              "  align-self: flex-end;\n",
              "  width: 50%;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
              "  align-self: flex-start;\n",
              "  width: 50%;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
              "  width: 0;\n",
              "}\n",
              "\n",
              "/* Serial-specific style estimator block */\n",
              "\n",
              "#sk-container-id-1 div.sk-serial {\n",
              "  display: flex;\n",
              "  flex-direction: column;\n",
              "  align-items: center;\n",
              "  background-color: var(--sklearn-color-background);\n",
              "  padding-right: 1em;\n",
              "  padding-left: 1em;\n",
              "}\n",
              "\n",
              "\n",
              "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
              "clickable and can be expanded/collapsed.\n",
              "- Pipeline and ColumnTransformer use this feature and define the default style\n",
              "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
              "*/\n",
              "\n",
              "/* Pipeline and ColumnTransformer style (default) */\n",
              "\n",
              "#sk-container-id-1 div.sk-toggleable {\n",
              "  /* Default theme specific background. It is overwritten whether we have a\n",
              "  specific estimator or a Pipeline/ColumnTransformer */\n",
              "  background-color: var(--sklearn-color-background);\n",
              "}\n",
              "\n",
              "/* Toggleable label */\n",
              "#sk-container-id-1 label.sk-toggleable__label {\n",
              "  cursor: pointer;\n",
              "  display: flex;\n",
              "  width: 100%;\n",
              "  margin-bottom: 0;\n",
              "  padding: 0.5em;\n",
              "  box-sizing: border-box;\n",
              "  text-align: center;\n",
              "  align-items: start;\n",
              "  justify-content: space-between;\n",
              "  gap: 0.5em;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
              "  font-size: 0.6rem;\n",
              "  font-weight: lighter;\n",
              "  color: var(--sklearn-color-text-muted);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
              "  /* Arrow on the left of the label */\n",
              "  content: \"▸\";\n",
              "  float: left;\n",
              "  margin-right: 0.25em;\n",
              "  color: var(--sklearn-color-icon);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
              "  color: var(--sklearn-color-text);\n",
              "}\n",
              "\n",
              "/* Toggleable content - dropdown */\n",
              "\n",
              "#sk-container-id-1 div.sk-toggleable__content {\n",
              "  max-height: 0;\n",
              "  max-width: 0;\n",
              "  overflow: hidden;\n",
              "  text-align: left;\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-0);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
              "  /* fitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-0);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-toggleable__content pre {\n",
              "  margin: 0.2em;\n",
              "  border-radius: 0.25em;\n",
              "  color: var(--sklearn-color-text);\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-0);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-0);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
              "  /* Expand drop-down */\n",
              "  max-height: 200px;\n",
              "  max-width: 100%;\n",
              "  overflow: auto;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
              "  content: \"▾\";\n",
              "}\n",
              "\n",
              "/* Pipeline/ColumnTransformer-specific style */\n",
              "\n",
              "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
              "  color: var(--sklearn-color-text);\n",
              "  background-color: var(--sklearn-color-unfitted-level-2);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
              "  background-color: var(--sklearn-color-fitted-level-2);\n",
              "}\n",
              "\n",
              "/* Estimator-specific style */\n",
              "\n",
              "/* Colorize estimator box */\n",
              "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-2);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
              "  /* fitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-2);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
              "#sk-container-id-1 div.sk-label label {\n",
              "  /* The background is the default theme color */\n",
              "  color: var(--sklearn-color-text-on-default-background);\n",
              "}\n",
              "\n",
              "/* On hover, darken the color of the background */\n",
              "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
              "  color: var(--sklearn-color-text);\n",
              "  background-color: var(--sklearn-color-unfitted-level-2);\n",
              "}\n",
              "\n",
              "/* Label box, darken color on hover, fitted */\n",
              "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
              "  color: var(--sklearn-color-text);\n",
              "  background-color: var(--sklearn-color-fitted-level-2);\n",
              "}\n",
              "\n",
              "/* Estimator label */\n",
              "\n",
              "#sk-container-id-1 div.sk-label label {\n",
              "  font-family: monospace;\n",
              "  font-weight: bold;\n",
              "  display: inline-block;\n",
              "  line-height: 1.2em;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-label-container {\n",
              "  text-align: center;\n",
              "}\n",
              "\n",
              "/* Estimator-specific */\n",
              "#sk-container-id-1 div.sk-estimator {\n",
              "  font-family: monospace;\n",
              "  border: 1px dotted var(--sklearn-color-border-box);\n",
              "  border-radius: 0.25em;\n",
              "  box-sizing: border-box;\n",
              "  margin-bottom: 0.5em;\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-0);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-estimator.fitted {\n",
              "  /* fitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-0);\n",
              "}\n",
              "\n",
              "/* on hover */\n",
              "#sk-container-id-1 div.sk-estimator:hover {\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-2);\n",
              "}\n",
              "\n",
              "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
              "  /* fitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-2);\n",
              "}\n",
              "\n",
              "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
              "\n",
              "/* Common style for \"i\" and \"?\" */\n",
              "\n",
              ".sk-estimator-doc-link,\n",
              "a:link.sk-estimator-doc-link,\n",
              "a:visited.sk-estimator-doc-link {\n",
              "  float: right;\n",
              "  font-size: smaller;\n",
              "  line-height: 1em;\n",
              "  font-family: monospace;\n",
              "  background-color: var(--sklearn-color-background);\n",
              "  border-radius: 1em;\n",
              "  height: 1em;\n",
              "  width: 1em;\n",
              "  text-decoration: none !important;\n",
              "  margin-left: 0.5em;\n",
              "  text-align: center;\n",
              "  /* unfitted */\n",
              "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
              "  color: var(--sklearn-color-unfitted-level-1);\n",
              "}\n",
              "\n",
              ".sk-estimator-doc-link.fitted,\n",
              "a:link.sk-estimator-doc-link.fitted,\n",
              "a:visited.sk-estimator-doc-link.fitted {\n",
              "  /* fitted */\n",
              "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
              "  color: var(--sklearn-color-fitted-level-1);\n",
              "}\n",
              "\n",
              "/* On hover */\n",
              "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
              ".sk-estimator-doc-link:hover,\n",
              "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
              ".sk-estimator-doc-link:hover {\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-3);\n",
              "  color: var(--sklearn-color-background);\n",
              "  text-decoration: none;\n",
              "}\n",
              "\n",
              "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
              ".sk-estimator-doc-link.fitted:hover,\n",
              "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
              ".sk-estimator-doc-link.fitted:hover {\n",
              "  /* fitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-3);\n",
              "  color: var(--sklearn-color-background);\n",
              "  text-decoration: none;\n",
              "}\n",
              "\n",
              "/* Span, style for the box shown on hovering the info icon */\n",
              ".sk-estimator-doc-link span {\n",
              "  display: none;\n",
              "  z-index: 9999;\n",
              "  position: relative;\n",
              "  font-weight: normal;\n",
              "  right: .2ex;\n",
              "  padding: .5ex;\n",
              "  margin: .5ex;\n",
              "  width: min-content;\n",
              "  min-width: 20ex;\n",
              "  max-width: 50ex;\n",
              "  color: var(--sklearn-color-text);\n",
              "  box-shadow: 2pt 2pt 4pt #999;\n",
              "  /* unfitted */\n",
              "  background: var(--sklearn-color-unfitted-level-0);\n",
              "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
              "}\n",
              "\n",
              ".sk-estimator-doc-link.fitted span {\n",
              "  /* fitted */\n",
              "  background: var(--sklearn-color-fitted-level-0);\n",
              "  border: var(--sklearn-color-fitted-level-3);\n",
              "}\n",
              "\n",
              ".sk-estimator-doc-link:hover span {\n",
              "  display: block;\n",
              "}\n",
              "\n",
              "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
              "\n",
              "#sk-container-id-1 a.estimator_doc_link {\n",
              "  float: right;\n",
              "  font-size: 1rem;\n",
              "  line-height: 1em;\n",
              "  font-family: monospace;\n",
              "  background-color: var(--sklearn-color-background);\n",
              "  border-radius: 1rem;\n",
              "  height: 1rem;\n",
              "  width: 1rem;\n",
              "  text-decoration: none;\n",
              "  /* unfitted */\n",
              "  color: var(--sklearn-color-unfitted-level-1);\n",
              "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
              "  /* fitted */\n",
              "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
              "  color: var(--sklearn-color-fitted-level-1);\n",
              "}\n",
              "\n",
              "/* On hover */\n",
              "#sk-container-id-1 a.estimator_doc_link:hover {\n",
              "  /* unfitted */\n",
              "  background-color: var(--sklearn-color-unfitted-level-3);\n",
              "  color: var(--sklearn-color-background);\n",
              "  text-decoration: none;\n",
              "}\n",
              "\n",
              "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
              "  /* fitted */\n",
              "  background-color: var(--sklearn-color-fitted-level-3);\n",
              "}\n",
              "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>StandardScaler()</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>StandardScaler</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.6/modules/generated/sklearn.preprocessing.StandardScaler.html\">?<span>Documentation for StandardScaler</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\"><pre>StandardScaler()</pre></div> </div></div></div></div>"
            ]
          },
          "metadata": {},
          "execution_count": 6
        }
      ],
      "execution_count": 6
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9vOk0Ffyc00D"
      },
      "source": [
        "### 构建数据集"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.511609Z",
          "start_time": "2025-01-17T03:09:34.506115Z"
        },
        "id": "INePw0xpc00D"
      },
      "source": [
        "from torch.utils.data import Dataset\n",
        "\n",
        "class HousingDataset(Dataset):\n",
        "    def __init__(self, mode='train'):\n",
        "        self.x, self.y = dataset_maps[mode]\n",
        "        self.x = torch.from_numpy(scaler.transform(self.x)).float()\n",
        "        self.y = torch.from_numpy(self.y).float().reshape(-1, 1)\n",
        "\n",
        "    def __len__(self):\n",
        "        return len(self.x)\n",
        "\n",
        "    def __getitem__(self, idx):\n",
        "        return self.x[idx], self.y[idx]\n",
        "\n",
        "\n",
        "train_ds = HousingDataset(\"train\")\n",
        "valid_ds = HousingDataset(\"valid\")\n",
        "test_ds = HousingDataset(\"test\")"
      ],
      "outputs": [],
      "execution_count": 7
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.517012Z",
          "start_time": "2025-01-17T03:09:34.511609Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0QmXrSYbc00E",
        "outputId": "d60f9418-fd36-4b40-a830-b6681df8acb6"
      },
      "source": [
        "train_ds[1]"
      ],
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "(tensor([-0.2981,  0.3523, -0.1092, -0.2506, -0.0341, -0.0060,  1.0806, -1.0611]),\n",
              " tensor([1.5140]))"
            ]
          },
          "metadata": {},
          "execution_count": 8
        }
      ],
      "execution_count": 8
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bDjyQ8yrc00F"
      },
      "source": [
        "### DataLoader"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.525510Z",
          "start_time": "2025-01-17T03:09:34.522016Z"
        },
        "id": "w6tu5Tw7c00F"
      },
      "source": [
        "from torch.utils.data import DataLoader\n",
        "\n",
        "\n",
        "batch_size = 8  #过大会导致GPU内存溢出，过小会导致训练时间过长\n",
        "train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)\n",
        "val_loader = DataLoader(valid_ds, batch_size=batch_size, shuffle=False)\n",
        "test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False)"
      ],
      "outputs": [],
      "execution_count": 9
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zWzZZhnLc00G"
      },
      "source": [
        "## 定义模型"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.540773Z",
          "start_time": "2025-01-17T03:09:34.537068Z"
        },
        "id": "TaaXJ0Nzc00G"
      },
      "source": [
        "#回归模型我们只需要1个数\n",
        "\n",
        "class WideDeep(nn.Module):\n",
        "    def __init__(self, input_dim=8):\n",
        "        super().__init__()\n",
        "        self.deep = nn.Sequential(\n",
        "            nn.Linear(input_dim, 30), #30个神经元\n",
        "            nn.ReLU(),\n",
        "            nn.Linear(30, 30), #30个神经元\n",
        "            nn.ReLU()\n",
        "            )\n",
        "        # pytorch 需要自行计算输出输出维度\n",
        "        self.output_layer = nn.Linear(30 + input_dim, 1)\n",
        "\n",
        "        # 初始化权重\n",
        "        self.init_weights()\n",
        "\n",
        "    def init_weights(self):\n",
        "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
        "        for m in self.modules():\n",
        "            if isinstance(m, nn.Linear):\n",
        "                nn.init.xavier_uniform_(m.weight)\n",
        "                nn.init.zeros_(m.bias)\n",
        "\n",
        "    def forward(self, x):\n",
        "        # x.shape [batch size, 8]\n",
        "        deep_output = self.deep(x)\n",
        "        # print(deep_output.shape)\n",
        "        # concat [batch size, 30] with x [batch size 8]，得到 [batch size, 38]\n",
        "        concat = torch.cat([x, deep_output], dim=1)\n",
        "        logits = self.output_layer(concat) # 输出层，输入维度是 38，输出维度是 1\n",
        "        # logits.shape [batch size, 1]\n",
        "        return logits"
      ],
      "outputs": [],
      "execution_count": 10
    },
    {
      "cell_type": "code",
      "source": [
        "# train_ds[0][0]\n",
        "#验证模型是否正确\n",
        "input=train_ds[0][0].reshape(1, -1)\n",
        "print(input.shape)\n",
        "model=WideDeep()\n",
        "out=model(input)\n",
        "out.shape"
      ],
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.547601Z",
          "start_time": "2025-01-17T03:09:34.541776Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "k5Uk0QTuc00G",
        "outputId": "facd7e9a-a5b1-4bba-b02a-bbce106ab7eb"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "torch.Size([1, 8])\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "torch.Size([1, 1])"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ],
      "execution_count": 11
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.550714Z",
          "start_time": "2025-01-17T03:09:34.547601Z"
        },
        "id": "o6SuAD3Rc00H"
      },
      "source": [
        "class EarlyStopCallback:\n",
        "    def __init__(self, patience=5, min_delta=0.01):\n",
        "        \"\"\"\n",
        "\n",
        "        Args:\n",
        "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
        "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
        "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
        "        \"\"\"\n",
        "        self.patience = patience\n",
        "        self.min_delta = min_delta\n",
        "        self.best_metric = -1\n",
        "        self.counter = 0\n",
        "\n",
        "    def __call__(self, metric):\n",
        "        if metric >= self.best_metric + self.min_delta:\n",
        "            # update best metric\n",
        "            self.best_metric = metric\n",
        "            # reset counter\n",
        "            self.counter = 0\n",
        "        else:\n",
        "            self.counter += 1\n",
        "\n",
        "    @property\n",
        "    def early_stop(self):\n",
        "        return self.counter >= self.patience\n"
      ],
      "outputs": [],
      "execution_count": 12
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:34.580312Z",
          "start_time": "2025-01-17T03:09:34.577985Z"
        },
        "id": "AiIkbkk8c00H"
      },
      "source": [
        "from sklearn.metrics import accuracy_score\n",
        "\n",
        "@torch.no_grad()\n",
        "def evaluating(model, dataloader, loss_fct):\n",
        "    loss_list = []\n",
        "    for datas, labels in dataloader:\n",
        "        datas = datas.to(device)\n",
        "        labels = labels.to(device)\n",
        "        # 前向计算\n",
        "        logits = model(datas)\n",
        "        loss = loss_fct(logits, labels)         # 验证集损失\n",
        "        loss_list.append(loss.item())\n",
        "\n",
        "    return np.mean(loss_list)\n"
      ],
      "outputs": [],
      "execution_count": 13
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FwoxSlTNc00H"
      },
      "source": [
        "## 训练"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:48.403198Z",
          "start_time": "2025-01-17T03:09:34.580312Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 49,
          "referenced_widgets": [
            "4fb7e9342ffc4d15a5caa51684a8db33",
            "6252e3f6eb1c471c9907eb4ebb40df07",
            "56469ab2feea4fe39c9fee30bc2b75b5",
            "6388a8ef23b543e4a9d629706f52813d",
            "1921d27f747742179a8e9207f929b81c",
            "3693d3ef9c9946ac87d3bf3a12de2c51",
            "6f2f843f181646ed9eac2863834e20bd",
            "e25282de661447259ecd6a254b36f0ac",
            "7d7f656a67e84d44a5e7a2e29ec0a70d",
            "9a8100969d314330aa3586b04e4eaf85",
            "001eeee3a1844bd584c4dc1d30e2b140"
          ]
        },
        "id": "_0gBaZhac00I",
        "outputId": "4d26f87a-5ea7-4a01-9911-6229d0b90fa0"
      },
      "source": [
        "# 训练\n",
        "def training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=None,\n",
        "    save_ckpt_callback=None,\n",
        "    early_stop_callback=None,\n",
        "    eval_step=500,\n",
        "    ):\n",
        "    record_dict = {\n",
        "        \"train\": [],\n",
        "        \"val\": []\n",
        "    }\n",
        "\n",
        "    global_step = 0\n",
        "    model.train()\n",
        "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
        "        for epoch_id in range(epoch):\n",
        "            # training\n",
        "            for datas, labels in train_loader:\n",
        "                datas = datas.to(device)\n",
        "                labels = labels.to(device)\n",
        "                # 梯度清空\n",
        "                optimizer.zero_grad()\n",
        "                # 模型前向计算\n",
        "                logits = model(datas)\n",
        "                # 计算损失\n",
        "                loss = loss_fct(logits, labels)\n",
        "                # 梯度回传\n",
        "                loss.backward()\n",
        "                # 调整优化器，包括学习率的变动等\n",
        "                optimizer.step()\n",
        "\n",
        "                loss = loss.cpu().item()\n",
        "                # record\n",
        "\n",
        "                record_dict[\"train\"].append({\n",
        "                    \"loss\": loss, \"step\": global_step\n",
        "                })\n",
        "\n",
        "                # evaluating\n",
        "                if global_step % eval_step == 0:\n",
        "                    model.eval()\n",
        "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
        "                    record_dict[\"val\"].append({\n",
        "                        \"loss\": val_loss, \"step\": global_step\n",
        "                    })\n",
        "                    model.train()\n",
        "\n",
        "                    # 早停 Early Stop\n",
        "                    if early_stop_callback is not None:\n",
        "                        early_stop_callback(-val_loss)\n",
        "                        if early_stop_callback.early_stop:\n",
        "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
        "                            return record_dict\n",
        "\n",
        "                # udate step\n",
        "                global_step += 1\n",
        "                pbar.update(1)\n",
        "                pbar.set_postfix({\"epoch\": epoch_id})\n",
        "\n",
        "    return record_dict\n",
        "\n",
        "\n",
        "epoch = 10\n",
        "\n",
        "model = WideDeep()\n",
        "\n",
        "# 1. 定义损失函数 采用交叉熵损失\n",
        "loss_fct = nn.MSELoss()\n",
        "# 2. 定义优化器 采用SGD\n",
        "# Optimizers specified in the torch.optim package\n",
        "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.0)\n",
        "\n",
        "# 3. early stop\n",
        "early_stop_callback = EarlyStopCallback(patience=10, min_delta=1e-3)\n",
        "\n",
        "model = model.to(device)\n",
        "record = training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    early_stop_callback=early_stop_callback,\n",
        "    eval_step=len(train_loader)\n",
        "    )"
      ],
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "  0%|          | 0/14520 [00:00<?, ?it/s]"
            ],
            "application/vnd.jupyter.widget-view+json": {
              "version_major": 2,
              "version_minor": 0,
              "model_id": "4fb7e9342ffc4d15a5caa51684a8db33"
            }
          },
          "metadata": {}
        }
      ],
      "execution_count": 14
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:48.475492Z",
          "start_time": "2025-01-17T03:09:48.404203Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 454
        },
        "id": "hy3soTatc00J",
        "outputId": "75fe3cc3-552b-4b9a-afb6-ac3ad388a3eb"
      },
      "source": [
        "#画线要注意的是损失是不一定在零到1之间的\n",
        "def plot_learning_curves(record_dict, sample_step=500):\n",
        "    # build DataFrame\n",
        "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
        "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
        "\n",
        "    # plot\n",
        "    for idx, item in enumerate(train_df.columns):\n",
        "        plt.plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
        "        plt.plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
        "        plt.grid()\n",
        "        plt.legend()\n",
        "        # plt.xticks(range(0, train_df.index[-1], 10*sample_step), range(0, train_df.index[-1], 10*sample_step))\n",
        "        plt.xlabel(\"step\")\n",
        "\n",
        "        plt.show()\n",
        "\n",
        "plot_learning_curves(record, sample_step=500)  #横坐标是 steps"
      ],
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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fjViAWjEQkTEh8KCLax7fGidz2O328nLzSMqOqrGZa3Dguu+M+fxqzvuvfde0tPTef7552nVqhVhYWFcfvnlp93JNTi46iICk8nk1k3aKkVFRfH7778ze/ZsfvnlFx599FEmTZrE0qVLiY2NJT09nd9++41ffvmFV199lYceeojFixfTvHlzt7elkt+MWFAZLDRiISLiMyaTiXBrkFs+wqwWl453pWqk1Wqt0ZbkCxYsYPz48Vx66aV07tyZRo0asWPHjjp8h1zTvn17FixYcEKb2rRp49zLIygoiMGDB/Pss8+yevVqduzYwa+//go4/j4GDBjA5MmTWbFiBVarlWnTpnm0zX40YuG4FWIqOAjFORAa4+MWiYhIfZWWlsbixYvZsWMHkZGRJx1NaN26NV9//TUjR47EZDLxyCOPeGTk4WTuueceevfuzRNPPMFVV13FwoULee2113jjjTcA+P7779m2bRsDBw6kQYMG/Pjjj9jtdtq2bcvixYuZOXMmF1xwAYmJiSxevJhDhw7Rvn17j7bZf0YsQqIoDop1PD6s2yEiInJy9957LxaLhQ4dOtCwYcOTzpl48cUXadCgAf3792fkyJEMHTqUHj16eK2dPXr04PPPP2fq1Kl06tSJRx99lMcff5zx48cDEBsby9dff835559P+/bteeutt/j000/p2LEj0dHRzJ07lxEjRtCmTRsefvhhXnjhBYYPH+7RNvvNiAVAXmhjQvOzIXMzNO3p6+aIiEg91aZNGxYuXFjluco362OlpaU5bytUmjBhQpXPj781Ut1yzuzs7Bq167zzzqO8vJzc3Fznc5dddhmXXXZZtcefffbZzJ49u9rX2rdvz/Tp02t0XXfynxELID+0sePB4U2+bYiIiEiA8q9gEeJYj6wlpyIiUh/dcsstREZGVvtxyy23+Lp5buFXt0LyQ5MdDxQsRESkHnr88ce59957q30tOjray63xDL8KFnkhFbdCsrY6NiMz131Ns4iIiLskJiaSmJjo62Z4lF/dCimyxmMEhUJ5KWTv9HVzREREAo5fBQtMZohr6Xis2yEiIiJe51/BgqOlvRUsREREvM+Pg4WWnIqIiHib/waLTFXfFBER8TY/DBaV26drxEJERDwjLS2Nl156qUbHmkwmvvnmG4+2pz7xu2DhnLxZcAiKPL/vvIiIiBzlf8EiJAqiKgtl6XaIiIiIN/lfsABIqJxnoZUhIiJS1TvvvENycvIJ25+PGjWKG264ga1btzJq1CiSkpKIjIykd+/ezJgxw23XX7NmDeeffz5hYWHEx8fz17/+lfz8fOfrs2fPZtCgQURFRREbG8uAAQPYudNRm2nVqlX86U9/IioqiujoaHr27MmyZcvc1jZ38NNg0cbxp+ZZiIh4l2FAaYF7PmyFrh1fza6i1bniiivIzMxk1qxZzueysrKYPn0648aNIz8/nxEjRjBz5kxWrFjBsGHDGDly5Em3VndFQUEBQ4cOpUGDBixdupQvvviCGTNmMHHiRADKysoYPXo0/fv3Z+XKlSxcuJC//vWvmEwmAMaNG0fTpk1ZunQpy5cv5+9//zvBwcF1bpc7+VVJbydnsNCIhYiIV9kK4V/JdT6NGYh19Yv+sQ+sEac9rEGDBgwfPpwpU6YwaNAgAL788ksSEhL405/+hNlspmvXrs7jn3jiCaZNm8Z3333nDAC1NWXKFIqLi/nvf/9LRISjra+99hojR47kmWeeITg4mJycHIYNG0bLli0xm820b9/e+fW7du3ivvvuo127dgC0bt26Tu3xBP8csVCRLBEROYVx48bx1VdfUVJSAsAnn3zC1VdfjdlsJj8/n3vvvZf27dsTGxtLZGQk69evd8uIxfr16+nataszVAAMGDAAu93Oxo0biYuL47rrruOyyy7j4osv5uWXX2b//v3OY++++27+8pe/MHjwYJ5++mm2bt1a5za5m3+PWGRtg/IysPhnN0VE6p3gcMfIQR3Z7XZy8/KIjorCbK7h78DB4TU+/8iRIzEMgx9++IHevXszb948/v3vfwNw7733kp6ezvPPP0+rVq0ICwvj8ssvp7S0tDZdcdn777/PDTfcwPz58/nss894+OGHSU9P56yzzmLSpEmMHTuWH374gZ9++onHHnuMqVOncumll3qlbTXhn++40U0gKAzKihybkcW39HWLREQCg8lUo9sRp2W3Q3C541w1DRYuCA0NZfTo0XzyySds2bKFtm3b0qNHDwAWLFjA+PHjnW/W+fn57Nixwy3Xbd++PR9++CEFBQXOUYsFCxZgNptp27at87guXbpw9tln849//IN+/foxZcoUzjrrLADatGlDmzZtuOuuuxgzZgwffPBBvQoW/nkrxGw+ujJEEzhFRKQa48aN44cf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fa9pW1yavGm1WunZsyczZ850Pme325k5c2aVWyMiIiISmFy+FXL33Xdz3XXX0atXL/r06cNLL71EQUEB119/vSfaJyIiImcQl4PFVVddxaFDh3j00UfJyMigW7duTJ8+naSkJE+0T0RERM4gtZq8OXHiRCZOnOjutoiIiMgZzuvbpleubq3p7NKastlsFBYWkpubW68mu3haoPYbArfv6rf6HQgCtd9Qf/te+b59uioVXg8WeXl5AKSkpHj70iIiIlJHeXl5xMTEnPR1lwpkuYPdbmffvn1ERUVhMpncdt7K+hi7d+92a32M+i5Q+w2B23f1W/0OBIHab6i/fTcMg7y8PJKTkzGbT76o1OsjFmazmaZNm3rs/NHR0fXqL8JbArXfELh9V78Di/odeOpj3081UlGpVtumi4iIiFRHwUJERETcxm+CRUhICI899li15cP9WaD2GwK37+q3+h0IArXfcOb33euTN0VERMR/+c2IhYiIiPiegoWIiIi4jYKFiIiIuI2ChYiIiLiN3wSL119/nbS0NEJDQ+nbty9LlizxdZNq7KmnnqJ3795ERUWRmJjIJZdcwsaNG6scU1xczIQJE4iPjycyMpLLLruMAwcOVDlm165dXHjhhYSHh5OYmMh9991HWVlZlWNmz55Njx49CAkJoVWrVnz44Yee7l6NPf3005hMJu68807nc/7a77179/LnP/+Z+Ph4wsLC6Ny5M8uWLXO+bhgGjz76KI0bNyYsLIzBgwezefPmKufIyspi3LhxREdHExsby4033kh+fn6VY1avXs0555xDaGgoKSkpPPvss17pX3XKy8t55JFHaN68OWFhYbRs2ZInnniiyr4D/tLvuXPnMnLkSJKTkzGZTHzzzTdVXvdmP7/44gvatWtHaGgonTt35scff3R7fyudqt82m40HHniAzp07ExERQXJyMtdeey379u2rcg5/6/fxbrnlFkwmEy+99FKV58/Efp+U4QemTp1qWK1W4/333zf++OMP46abbjJiY2ONAwcO+LppNTJ06FDjgw8+MNauXWusXLnSGDFihNGsWTMjPz/fecwtt9xipKSkGDNnzjSWLVtmnHXWWUb//v2dr5eVlRmdOnUyBg8ebKxYscL48ccfjYSEBOPBBx90HrNt2zYjPDzcuPvuu41169YZr776qmGxWIzp06d7tb/VWbJkiZGWlmZ06dLFuOOOO5zP+2O/s7KyjNTUVGP8+PHG4sWLjW3bthk///yzsWXLFucxTz/9tBETE2N88803xqpVq4yLL77YaN68uVFUVOQ8ZtiwYUbXrl2NRYsWGfPmzTNatWpljBkzxvl6Tk6OkZSUZIwbN85Yu3at8emnnxphYWHG22+/7dX+VnryySeN+Ph44/vvvze2b99ufPHFF0ZkZKTx8ssvO4/xl37/+OOPxkMPPWR8/fXXBmBMmzatyuve6ueCBQsMi8ViPPvss8a6deuMhx9+2AgODjbWrFnj9X5nZ2cbgwcPNj777DNjw4YNxsKFC40+ffoYPXv2rHIOf+v3sb7++muja9euRnJysvHvf/+7ymtnYr9Pxi+CRZ8+fYwJEyY4Py8vLzeSk5ONp556yoetqr2DBw8agDFnzhzDMBz/IYODg40vvvjCecz69esNwFi4cKFhGI5/2Gaz2cjIyHAe8+abbxrR0dFGSUmJYRiGcf/99xsdO3ascq2rrrrKGDp0qKe7dEp5eXlG69atjfT0dOPcc891Bgt/7fcDDzxgnH322Sd93W63G40aNTKee+4553PZ2dlGSEiI8emnnxqGYRjr1q0zAGPp0qXOY3766SfDZDIZe/fuNQzDMN544w2jQYMGzu9D5bXbtm3r7i7VyIUXXmjccMMNVZ4bPXq0MW7cOMMw/Lffx7/ReLOfV155pXHhhRdWaU/fvn2Nm2++2a19rM6p3mArLVmyxACMnTt3Gobh3/3es2eP0aRJE2Pt2rVGampqlWDhD/0+1hl/K6S0tJTly5czePBg53Nms5nBgwezcOFCH7as9nJycgCIi4sDYPny5dhstip9bNeuHc2aNXP2ceHChXTu3JmkpCTnMUOHDiU3N5c//vjDecyx56g8xtffpwkTJnDhhRee0DZ/7fd3331Hr169uOKKK0hMTKR79+68++67zte3b99ORkZGlTbHxMTQt2/fKv2OjY2lV69ezmMGDx6M2Wxm8eLFzmMGDhyI1Wp1HjN06FA2btzIkSNHPN3NE/Tv35+ZM2eyadMmAFatWsX8+fMZPnw44L/9Pp43+1nf/u0fLycnB5PJRGxsLOC//bbb7VxzzTXcd999dOzY8YTX/a3fZ3ywOHz4MOXl5VXeWACSkpLIyMjwUatqz263c+eddzJgwAA6deoEQEZGBlar1fmfr9KxfczIyKj2e1D52qmOyc3NpaioyBPdOa2pU6fy+++/89RTT53wmr/2e9u2bbz55pu0bt2an3/+mVtvvZXbb7+djz76qEq7T/VvOiMjg8TExCqvBwUFERcX59L3xpv+/ve/c/XVV9OuXTuCg4Pp3r07d955J+PGjavSJn/r9/G82c+THVMfvg/FxcU88MADjBkzxrnRlr/2+5lnniEoKIjbb7+92tf9rd9e391UTm3ChAmsXbuW+fPn+7opHrd7927uuOMO0tPTCQ0N9XVzvMZut9OrVy/+9a9/AdC9e3fWrl3LW2+9xXXXXefj1nnO559/zieffMKUKVPo2LEjK1eu5M477yQ5Odmv+y0nstlsXHnllRiGwZtvvunr5njU8uXLefnll/n9998xmUy+bo5XnPEjFgkJCVgslhNWChw4cIBGjRr5qFW1M3HiRL7//ntmzZpVZWv5Ro0aUVpaSnZ2dpXjj+1jo0aNqv0eVL52qmOio6MJCwtzd3dOa/ny5Rw8eJAePXoQFBREUFAQc+bM4ZVXXiEoKIikpCS/7Hfjxo3p0KFDlefat2/Prl27gKPtPtW/6UaNGnHw4MEqr5eVlZGVleXS98ab7rvvPueoRefOnbnmmmu46667nKNV/trv43mznyc7xpffh8pQsXPnTtLT06tsC+6P/Z43bx4HDx6kWbNmzp9zO3fu5J577iEtLc3ZXn/q9xkfLKxWKz179mTmzJnO5+x2OzNnzqRfv34+bFnNGYbBxIkTmTZtGr/++ivNmzev8nrPnj0JDg6u0seNGzeya9cuZx/79evHmjVrqvzjrPxPW/km1q9fvyrnqDzGV9+nQYMGsWbNGlauXOn86NWrF+PGjXM+9sd+Dxgw4ITlxJs2bSI1NRWA5s2b06hRoyptzs3NZfHixVX6nZ2dzfLly53H/Prrr9jtdvr27es8Zu7cudhsNucx6enptG3blgYNGnisfydTWFiI2Vz1R47FYsFutwP+2+/jebOf9e3ffmWo2Lx5MzNmzCA+Pr7K6/7Y72uuuYbVq1dX+TmXnJzMfffdx88//+xsr1/126tTRT1k6tSpRkhIiPHhhx8a69atM/76178asbGxVVYK1Ge33nqrERMTY8yePdvYv3+/86OwsNB5zC233GI0a9bM+PXXX41ly5YZ/fr1M/r16+d8vXLZ5QUXXGCsXLnSmD59utGwYcNql13ed999xvr1643XX3+93iw3rXTsqhDD8M9+L1myxAgKCjKefPJJY/PmzcYnn3xihIeHGx9//LHzmKefftqIjY01vv32W2P16tXGqFGjql2O2L17d2Px4sXG/PnzjdatW1dZnpadnW0kJSUZ11xzjbF27Vpj6tSpRnh4uM+Wm1533XVGkyZNnMtNv/76ayMhIcG4//77ncf4S7/z8vKMFStWGCtWrDAA48UXXzRWrFjhXP3grX4uWLDACAoKMp5//nlj/fr1xmOPPebR5Yen6ndpaalx8cUXG02bNjVWrlxZ5WfdsSsd/K3f1Tl+VciZ2u+T8YtgYRiG8eqrrxrNmjUzrFar0adPH2PRokW+blKNAdV+fPDBB85jioqKjL/97W9GgwYNjPDwcOPSSy819u/fX+U8O3bsMIYPH26EhYUZCQkJxj333GPYbLYqx8yaNcvo1q2bYbVajRYtWlS5Rn1wfLDw137/73//Mzp16mSEhIQY7dq1M955550qr9vtduORRx4xkpKSjJCQEGPQoEHGxo0bqxyTmZlpjBkzxoiMjDSio6ON66+/3sjLy6tyzKpVq4yzzz7bCAkJMZo0aWI8/fTTHu/byeTm5hp33HGH0axZMyM0NNRo0aKF8dBDD1V5U/GXfs+aNava/9PXXXedYRje7efnn39utGnTxrBarUbHjh2NH374wSf93r59+0l/1s2aNctv+12d6oLFmdjvk9G26SIiIuI2Z/wcCxEREak/FCxERETEbRQsRERExG0ULERERMRtFCxERETEbRQsRERExG0ULERERMRtFCxERETEbRQsRMQl48eP55JLLvF1M0SknlKwEBEREbdRsBCRan355Zd07tyZsLAw4uPjGTx4MPfddx8fffQR3377LSaTCZPJxOzZswHYvXs3V155JbGxscTFxTFq1Ch27NjhPF/lSMfkyZNp2LAh0dHR3HLLLZSWlvqmgyLiEUG+boCI1D/79+9nzJgxPPvss1x66aXk5eUxb948rr32Wnbt2kVubi4ffPABAHFxcdhsNoYOHUq/fv2YN28eQUFB/POf/2TYsGGsXr0aq9UKwMyZMwkNDWX27Nns2LGD66+/nvj4eJ588klfdldE3EjBQkROsH//fsrKyhg9ejSpqakAdO7cGYCwsDBKSkpo1KiR8/iPP/4Yu93Oe++9h8lkAuCDDz4gNjaW2bNnc8EFFwBgtVp5//33CQ8Pp2PHjjz++OPcd999PPHEE5jNGkAV8Qf6nywiJ+jatSuDBg2ic+fOXHHFFbz77rscOXLkpMevWrWKLVu2EBUVRWRkJJGRkcTFxVFcXMzWrVurnDc8PNz5eb9+/cjPz2f37t0e7Y+IeI9GLETkBBaLhfT0dH777Td++eUXXn31VR566CEWL15c7fH5+fn07NmTTz755ITXGjZs6Onmikg9omAhItUymUwMGDCAAQMG8Oijj5Kamsq0adOwWq2Ul5dXObZHjx589tlnJCYmEh0dfdJzrlq1iqKiIsLCwgBYtGgRkZGRpKSkeLQvIuI9uhUiIidYvHgx//rXv1i2bBm7du3i66+/5tChQ7Rv3560tDRWr17Nxo0bOXz4MDabjXHjxpGQkMCoUaOYN28e27dvZ/bs2dx+++3s2bPHed7S0lJuvPFG1q1bx48//shjjz3GxIkTNb9CxI9oxEJEThAdHc3cuXN56aWXyM3NJTU1lRdeeIHhw4fTq1cvZs+eTa9evcjPz2fWrFmcd955zJ07lwceeIDRo0eTl5dHkyZNGDRoUJURjEGDBtG6dWsGDhxISUkJY8aMYdKkSb7rqIi4nckwDMPXjRAR/zd+/Hiys7P55ptvfN0UEfEgjT+KiIiI2yhYiIiIiNvoVoiIiIi4jUYsRERExG0ULERERMRtFCxERETEbRQsRERExG0ULERERMRtFCxERETEbRQsRERExG0ULERERMRtFCxERETEbf4fbL0RvhZhopcAAAAASUVORK5CYII=\n"
          },
          "metadata": {}
        }
      ],
      "execution_count": 15
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3_rxM1a_c00J"
      },
      "source": [
        "## 测试集"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T03:09:48.527250Z",
          "start_time": "2025-01-17T03:09:48.476008Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Km8Mhs-tc00K",
        "outputId": "ed31230e-0e9a-44e6-8b1c-517447b1cf2b"
      },
      "source": [
        "model.eval()\n",
        "loss = evaluating(model, val_loader, loss_fct)\n",
        "print(f\"loss:     {loss:.4f}\")"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "loss:     0.4784\n"
          ]
        }
      ],
      "execution_count": 16
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "UQSagvuAeoF8"
      },
      "execution_count": null,
      "outputs": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.8"
    },
    "orig_nbformat": 4,
    "colab": {
      "provenance": [],
      "gpuType": "T4"
    },
    "accelerator": "GPU",
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "4fb7e9342ffc4d15a5caa51684a8db33": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HBoxModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HBoxModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HBoxView",
            "box_style": "",
            "children": [
              "IPY_MODEL_6252e3f6eb1c471c9907eb4ebb40df07",
              "IPY_MODEL_56469ab2feea4fe39c9fee30bc2b75b5",
              "IPY_MODEL_6388a8ef23b543e4a9d629706f52813d"
            ],
            "layout": "IPY_MODEL_1921d27f747742179a8e9207f929b81c"
          }
        },
        "6252e3f6eb1c471c9907eb4ebb40df07": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_3693d3ef9c9946ac87d3bf3a12de2c51",
            "placeholder": "​",
            "style": "IPY_MODEL_6f2f843f181646ed9eac2863834e20bd",
            "value": "100%"
          }
        },
        "56469ab2feea4fe39c9fee30bc2b75b5": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "FloatProgressModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "FloatProgressModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "ProgressView",
            "bar_style": "success",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_e25282de661447259ecd6a254b36f0ac",
            "max": 14520,
            "min": 0,
            "orientation": "horizontal",
            "style": "IPY_MODEL_7d7f656a67e84d44a5e7a2e29ec0a70d",
            "value": 14520
          }
        },
        "6388a8ef23b543e4a9d629706f52813d": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_9a8100969d314330aa3586b04e4eaf85",
            "placeholder": "​",
            "style": "IPY_MODEL_001eeee3a1844bd584c4dc1d30e2b140",
            "value": " 14520/14520 [01:15&lt;00:00, 216.63it/s, epoch=9]"
          }
        },
        "1921d27f747742179a8e9207f929b81c": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "3693d3ef9c9946ac87d3bf3a12de2c51": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "6f2f843f181646ed9eac2863834e20bd": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "e25282de661447259ecd6a254b36f0ac": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "7d7f656a67e84d44a5e7a2e29ec0a70d": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "ProgressStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "ProgressStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "bar_color": null,
            "description_width": ""
          }
        },
        "9a8100969d314330aa3586b04e4eaf85": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "001eeee3a1844bd584c4dc1d30e2b140": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        }
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}